home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 May: Tool Chest / Dev.CD May 97 TC.toast / Sample Code / Development Tools & Languages / AppsToGo / DTS.Lib / ListControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-22  |  45.7 KB  |  1,810 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         listcontrol.c
  5. ** Written by:      Eric Soldan
  6. **
  7. ** Copyright © 1991 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /*
  20. **
  21. ** To create a List control, you only need a single call.  For example:
  22. **
  23. **    list = CLNew(rViewCtl,                                    Resource ID of view control for List control.
  24. **                 true,                                         Control initially visible.
  25. **                 &viewRct,                                    View rect of list.
  26. **                 numRows,                                    Number of rows to create List with.
  27. **                 numCols,                                    Number of columns to create List with.
  28. **                 cellHeight,
  29. **                 cellWidth,
  30. **                 theLProc,                                    Custom List procedure resource ID.
  31. **                 window,                                    Window to hold List control.
  32. **                 clHScroll | clShowActive | clActive        Horizontal scrollbar, active List.
  33. **    );
  34. **
  35.  
  36. ** If the CLNew call succeeds, you then have a List control in your
  37. ** window.  It will be automatically disposed of when you close the window.
  38. ** If you don't want this to happen, then you can detach it from the
  39. ** view control which owns it.  To do this, you would to the following:
  40. **
  41. **  viewCtl = CLViewFromList(theListHndl);
  42. **  if (viewCtl) SetControlReference(viewCtl, nil);
  43. **
  44. ** The view control keeps a reference to the List record in the refCon.
  45. ** If the refCon is cleared, then the view control does nothing.  So, all that
  46. ** is needed to detach a List record from a view control is to set the
  47. ** view control's refCon nil.  Now if you close the window, you will still
  48. ** have the List record.
  49. **
  50. **
  51. ** To remove a List control completely from a window, just dispose of the view
  52. ** control that holds the List record.  To do this, just do something like the below:
  53. **
  54. **  DisposeControl(CLViewFromList(theListHndl));
  55. **
  56. ** This completely disposes of the List control.
  57. **
  58. **
  59. ** Events for the List record are handled nearly automatically.  Just make the
  60. ** following call:
  61. **
  62. **  CLClick(window, eventPtr, &action);
  63. **
  64. ** If the event was handled, true is returned.  If the event is false, then the
  65. ** event doesn't belong to a List control, and further processing of the event
  66. ** should be done.
  67. */
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72.  
  73.  
  74.  
  75. #ifndef __CONTROLS__
  76. #include <Controls.h>
  77. #endif
  78.  
  79. #ifndef __DTSLib__
  80. #include "DTS.Lib.h"
  81. #endif
  82.  
  83. #ifndef __ERRORS__
  84. #include <Errors.h>
  85. #endif
  86.  
  87. #ifndef __LISTCONTROL__
  88. #include "ListControl.h"
  89. #endif
  90.  
  91. #ifndef __LOWMEM__
  92. #include <LowMem.h>
  93. #endif
  94.  
  95. #ifndef __MEMORY__
  96. #include <Memory.h>
  97. #endif
  98.  
  99. #ifndef __PACKAGES__
  100. #include <Packages.h>
  101. #endif
  102.  
  103. #ifndef __RESOURCES__
  104. #include <Resources.h>
  105. #endif
  106.  
  107. #ifndef __TEXTUTILS__
  108. #include "TextUtils.h"
  109. #endif
  110.  
  111. #ifndef __UTILITIES__
  112. #include "Utilities.h"
  113. #endif
  114.  
  115.  
  116.  
  117. /*****************************************************************************/
  118.  
  119.  
  120.  
  121. #define kListPosTextLen 32
  122. #define kPrevSel        16
  123.  
  124. #ifdef powerc
  125. #pragma options align=mac68k
  126. #endif
  127. typedef struct cdefRsrcJMP {
  128.     long    jsrInst;
  129.     long    moveInst;
  130.     short    jmpInst;
  131.     long    jmpAddress;
  132. } cdefRsrcJMP;
  133. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  134. #ifdef powerc
  135. #pragma options align=reset
  136. #endif
  137.  
  138.  
  139.  
  140. /*****************************************************************************/
  141.  
  142.  
  143.  
  144. short    gListCtl = rListCtl;
  145.  
  146. static long                        gLastKeyTime;
  147. static char                        gListPosText[kListPosTextLen];
  148. static short                    gListPosTextLen;
  149. static cdefRsrcJMPHndl            gCDEF;
  150. static CLGetCompareDataProcPtr    gGetCompareDataProc;
  151. static CLDoCompareDataProcPtr    gDoCompareDataProc;
  152. static ListHandle                gCLVList;
  153. static ControlHandle            gVFLView;
  154. static ListHandle                gVFLList;
  155. static Rect                        gLRect;
  156.  
  157. static void            CLVScrollContent(short h, short v);
  158. static WindowPtr    CLVSetClip(ListHandle list);
  159.  
  160. static void            CLInitialize(void);
  161. static void            CLBorderDraw(ListHandle listHndl);
  162. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm);
  163. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen);
  164.  
  165. static void                dummyCLActivate(Boolean makeActive, ListHandle listHndl);
  166. static Boolean            dummyCLClick(WindowPtr window, EventRecord *event, short *action);
  167. static ControlHandle    dummyCLCtlHit(void);
  168. static ListHandle        dummyCLFindActive(WindowPtr window);
  169. static Boolean            dummyCLKey(WindowPtr window, EventRecord *event);
  170. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive);
  171. static ControlHandle    dummyCLViewFromList(ListHandle listHndl);
  172. static ListHandle        dummyCLWindActivate(WindowPtr window, Boolean displayIt);
  173.  
  174. CLActivateProcPtr        gclActivate       = dummyCLActivate;
  175. CLClickProcPtr            gclClick          = dummyCLClick;
  176. CLCtlHitProcPtr            gclCtlHit         = dummyCLCtlHit;
  177. CLFindActiveProcPtr        gclFindActive     = dummyCLFindActive;
  178. CLKeyProcPtr            gclKey            = dummyCLKey;
  179. CLNextProcPtr            gclNext           = dummyCLNext;
  180. CLViewFromListProcPtr    gclViewFromList   = dummyCLViewFromList;
  181. CLWindActivateProcPtr    gclWindActivate   = dummyCLWindActivate;
  182.  
  183.  
  184. CLVVariableSizeCellsProcPtr    gclvVariableSizeCells = nil;
  185. CLVGetCellRectProcPtr        gclvGetCellRect       = nil;
  186. CLVUpdateProcPtr            gclvUpdate            = nil;
  187. CLVAutoScrollProcPtr        gclvAutoScroll        = nil;
  188. CLVSetSelectProcPtr            gclvSetSelect         = nil;
  189. CLVClickProcPtr                gclvClick             = nil;
  190. CLVAdjustScrollBarsProcPtr  gclvAdjustScrollBars  = nil;
  191.  
  192.  
  193. extern short    gPrintPage;        /* Non-zero means we are printing. */
  194.  
  195.  
  196.  
  197. /*****************************************************************************/
  198.  
  199.  
  200.  
  201. static ListHandle        gFoundLHndl;
  202.     /* Global value used to return info from the List control proc. */
  203.  
  204. static ControlHandle    gFoundViewCtl;
  205.     /* Global value used to return info from the List control proc. */
  206.  
  207.  
  208.  
  209. /*****************************************************************************/
  210. /*****************************************************************************/
  211.  
  212. #ifdef applec
  213. #pragma segment ATGListControl
  214. #endif
  215.  
  216. /*****************************************************************************/
  217. /*****************************************************************************/
  218.  
  219.  
  220.  
  221. /* Instead of calling the functions directly, you can reference the global
  222. ** proc pointers that reference the functions.  This keeps everything from
  223. ** being linked in.  The default global proc pointers point to dummy functions
  224. ** that behave as if there aren't any list controls.  The calls can still be
  225. ** made, yet the runtime behavior is such that it will operate as if there
  226. ** no instances of the List control.  This allows intermediate code to access
  227. ** the functions or not without automatically linking in all sorts of stuff
  228. ** into the application that isn't desired.  To change the global proc pointers
  229. ** so that they point to the actual functions, just call CLInitialize() once
  230. ** in the beginning of the application.  If CLInitialize() is referenced, it will
  231. ** get linked in.  In turn, everything that it references directly or indirectly
  232. ** will get linked in. */
  233.  
  234. static void    CLInitialize(void)
  235. {
  236.     if (gclActivate != CLActivate) {
  237.         gclActivate       = CLActivate;
  238.         gclClick          = CLClick;
  239.         gclCtlHit         = CLCtlHit;
  240.         gclFindActive     = CLFindActive;
  241.         gclKey            = CLKey;
  242.         gclNext           = CLNext;
  243.         gclViewFromList   = CLViewFromList;
  244.         gclWindActivate   = CLWindActivate;
  245.     }
  246. }
  247.  
  248.  
  249.  
  250. /*****************************************************************************/
  251.  
  252.  
  253.  
  254. /* Activate this List record.  Activation is NOT done by calling LActivate().
  255. ** The active control is indicated by the 2-pixel thick border around the
  256. ** List control.  This allows all List controls in a window to display which
  257. ** cells are selected.  This behavior can be overridden by calling LActivate()
  258. ** on the List record for List controls.
  259. ** Human interface dictates that only at most a single List control has this
  260. ** active border.  For this reason, this function scans for other List
  261. ** controls in the window and removes the border from any other that it finds. */
  262.  
  263. void    CLActivate(Boolean makeActive, ListHandle listHndl)
  264. {
  265.     WindowPtr        window, oldPort;
  266.     ControlHandle    viewCtl;
  267.     short            oldDisplay, newDisplay;
  268.     ListHandle        list;
  269.     CLDataHndl        listData;
  270.  
  271.     if (listHndl) {
  272.         window = (WindowPtr)(*listHndl)->port;
  273.         for (viewCtl = nil;;) {
  274.             viewCtl = CLNext(window, &list, viewCtl, 1, false);
  275.             if (!viewCtl) break;
  276.             listData   = (CLDataHndl)(*viewCtl)->contrlData;
  277.             oldDisplay = (*listData)->mode;
  278.             newDisplay = (oldDisplay & (0xFFFF - clActive));
  279.             if (makeActive)
  280.                 if (list == listHndl)
  281.                     newDisplay |= clActive;
  282.             if (oldDisplay != newDisplay) {
  283.                 (*listData)->mode = newDisplay;
  284.                 GetPort(&oldPort);
  285.                 SetPort(window);
  286.                 CLBorderDraw(list);
  287.                 SetPort(oldPort);
  288.             }
  289.         }
  290.     }
  291. }
  292.  
  293.  
  294.  
  295. static void    dummyCLActivate(Boolean makeActive, ListHandle listHndl)
  296. {
  297. #ifndef __MWERKS__
  298. #pragma unused (makeActive, listHndl)
  299. #endif
  300. }
  301.  
  302.  
  303.  
  304. /*****************************************************************************/
  305.  
  306.  
  307.  
  308. static void    CLBorderDraw(ListHandle listHndl)
  309. {
  310.     ControlHandle    viewCtl;
  311.     WindowPtr        oldPort, listPort;
  312.     short            displayInfo;
  313.     PenState        oldPen;
  314.     CLDataHndl        listData;
  315.  
  316.     SetRect(&gLRect, 0, 0, 0, 0);
  317.     if (listHndl) {
  318.         viewCtl = CLViewFromList(listHndl);
  319.         if (viewCtl) {
  320.             if ((*viewCtl)->contrlVis) {
  321.                 GetPort(&oldPort);
  322.                 SetPort(listPort = (*listHndl)->port);
  323.                 GetPenState(&oldPen);
  324.                 PenNormal();
  325.                 listData    = (CLDataHndl)(*viewCtl)->contrlData;
  326.                 displayInfo = (*listData)->mode;
  327.                 gLRect = (*listHndl)->rView;
  328.                 InsetRect(&gLRect, -1, -1);
  329.                 FrameRect(&gLRect);
  330.                 if (displayInfo & clShowActive) {
  331.                     gLRect = (*listHndl)->rView;
  332.                     InsetRect(&gLRect, -4, -4);
  333.                     if ((*listHndl)->vScroll)
  334.                         gLRect.right  += 15;
  335.                     if ((*listHndl)->hScroll)
  336.                         gLRect.bottom += 15;
  337.                     PenSize(2, 2);
  338.                     if ((!((WindowPeek)listPort)->hilited) || (!(displayInfo & clActive)))
  339.                         PenPat((ConstPatternParam)&qd.white);
  340.                     FrameRect(&gLRect);
  341.                 }
  342.                 SetPenState(&oldPen);
  343.                 SetPort(oldPort);
  344.             }
  345.         }
  346.     }
  347. }
  348.  
  349.  
  350.  
  351. /*****************************************************************************/
  352.  
  353.  
  354.  
  355. /* This is called when a mouseDown occurs in the content of a window.  It
  356. ** returns true if the mouseDown caused a List action to occur.  Events
  357. ** that are handled include if the user clicks on a scrollbar that is
  358. ** associated with a List control. */
  359.  
  360. Boolean    CLClick(WindowPtr window, EventRecord *event, short *action)
  361. {
  362.     WindowPtr        oldPort;
  363.     Point            mouseLoc;
  364.     ListHandle        list;
  365.     ControlHandle    ctlHit, viewCtl;
  366.     CLDataHndl        listData;
  367.     short            mode;
  368.     unsigned char    ohlt;
  369.  
  370.     if (action)
  371.         *action = 0;
  372.     gLastKeyTime = 0;
  373.  
  374.     GetPort(&oldPort);
  375.     if (!((WindowPeek)window)->hilited) return(false);
  376.  
  377.     SetPort(window);
  378.     mouseLoc = event->where;
  379.     GlobalToLocal(&mouseLoc);
  380.  
  381.     if (!(viewCtl = CLFindCtl(window, event, &list, &ctlHit))) return(false);
  382.  
  383.     if (!list) {
  384.         SetPort(oldPort);
  385.         return(false);
  386.     }        /* Didn't hit list control or related scrollbar.  No action taken. */
  387.  
  388.     if (CLFindActive(window) != list) {            /* If not active list control, activate it.       */
  389.         if ((*viewCtl)->contrlHilite != 255) {
  390.             CLActivate(true, list);                /* Now is the active list control.               */
  391.             if (action)                            /* CLClick can be called again if the control  */
  392.                 *action = -1;                    /* activates and operates with the same click. */
  393.             SetPort(oldPort);
  394.             return(true);
  395.         }
  396.     }
  397.  
  398.     UseControlStyle(viewCtl);
  399.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  400.     mode     = (*listData)->mode;
  401.     if (mode & clVariable) {
  402.         if ((*gclvClick)(mouseLoc, event->modifiers, list))
  403.             if (action)
  404.                 *action = 1;        /* If double-click, then return that it was. */
  405.     }
  406.     else {
  407.         ohlt = (*viewCtl)->contrlHilite;
  408.         (*viewCtl)->contrlHilite = -1;
  409.             /* System 6 has a bug.  It calls FindControl to see if a List scrollbar was hit.  If it finds a control,
  410.             ** it assumes that it is going to be a list control of its own.  If it can't match against either the vert
  411.             ** or horiz scrollbar, it exits.  This is of course really bad for a list control, since the body of the
  412.             ** list is a control, and therefore FindControl will return something that doesn't match the scrollbars.
  413.             ** This only fixes the problem if the list control isn't on top of any other controls.  Controls beneath
  414.             ** the list will need to be temporarily inactivated by the application. */
  415.  
  416.         if (LClick(mouseLoc, event->modifiers, list))
  417.             if (action)
  418.                 *action = 1;        /* If double-click, then return that it was. */
  419.         (*viewCtl)->contrlHilite = ohlt;
  420.     }
  421.     UseControlStyle(nil);
  422.  
  423.     SetPort(oldPort);
  424.     return(true);
  425. }
  426.  
  427.  
  428.  
  429. static Boolean    dummyCLClick(WindowPtr window, EventRecord *event, short *action)
  430. {
  431. #ifndef __MWERKS__
  432. #pragma unused (window, event)
  433. #endif
  434.  
  435.     if (action)
  436.         *action = 0;
  437.     return(false);
  438. }
  439.  
  440.  
  441.  
  442. /*****************************************************************************/
  443.  
  444.  
  445.  
  446. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm)
  447. {
  448. #ifndef __MWERKS__
  449. #pragma unused (varCode)
  450. #endif
  451.  
  452.     Rect            viewRct;
  453.     ListHandle        list;
  454.     WindowPtr        curPort, ww;
  455.     ControlHandle    vScroll, hScroll;
  456.     CLDataHndl        listData;
  457.  
  458.     list = (ListHandle)GetControlReference(ctl);
  459.     if (list)
  460.         viewRct = (*list)->rView;
  461.     else
  462.         SetRect(&viewRct, 0, 0, 0, 0);
  463.  
  464.     switch (msg) {
  465.         case drawCntl:
  466.             GetPort(&curPort);
  467.             vScroll = (*list)->vScroll;
  468.             if (vScroll) {
  469.                 ww = (*vScroll)->contrlOwner;
  470.                 if (!((WindowPeek)ww)->hilited) (*list)->vScroll = nil;
  471.             }
  472.             hScroll = (*list)->hScroll;
  473.             if (hScroll) {
  474.                 ww = (*hScroll)->contrlOwner;
  475.                 if (!((WindowPeek)ww)->hilited) (*list)->hScroll = nil;
  476.             }
  477.             CLUpdate(curPort->visRgn, list);
  478.             (*list)->vScroll = vScroll;
  479.             (*list)->hScroll = hScroll;
  480.             CLBorderDraw(list);
  481.             break;
  482.  
  483.         case testCntl:
  484.             if (PtInRect(*(Point *)&parm, &viewRct)) {
  485.                 gFoundViewCtl = ctl;
  486.                 gFoundLHndl   = list;
  487.                 return(1);
  488.             }
  489.             return(0);
  490.             break;
  491.  
  492.         case calcCRgns:
  493.         case calcCntlRgn:
  494.             if (msg == calcCRgns)
  495.                 parm &= 0x00FFFFFF;
  496.             RectRgn((RgnHandle)parm, &viewRct);
  497.             break;
  498.  
  499.         case initCntl:
  500.             break;
  501.  
  502.         case dispCntl:
  503.             if (list) {
  504.                 GetPort(&curPort);
  505.                 SetPort((*list)->port);
  506.                 gVFLView = ctl;
  507.                 gVFLList = list;
  508.                 listData = (CLDataHndl)(*ctl)->contrlData;
  509.                 (*listData)->mode &= (0xFFFF - clActive);
  510.                 CLBorderDraw(list);
  511.                 EraseRect(&gLRect);
  512.                 InvalRect(&gLRect);
  513.                 gVFLView = nil;
  514.                 gVFLList = nil;
  515.                 LDispose(list);
  516.                 DisposeHandle((Handle)(*ctl)->contrlData);
  517.                 SetPort(curPort);
  518.             }
  519.             break;
  520.  
  521.         case posCntl:
  522.             break;
  523.  
  524.         case thumbCntl:
  525.             break;
  526.  
  527.         case dragCntl:
  528.             break;
  529.  
  530.         case autoTrack:
  531.             break;
  532.     }
  533.  
  534.     return(0);
  535. }
  536.  
  537.  
  538.  
  539. /*****************************************************************************/
  540.  
  541.  
  542.  
  543. /* The List control that was hit by calling FindControl is saved in a
  544. ** global variable, since the CDEF has no way of returning what kind it was.
  545. ** To determine that it was a List control that was hit, first call this
  546. ** function.  The first call returns the old value in the global variable,
  547. ** plus it resets the global to nil.  Then call FindControl(), and then
  548. ** call this function again.  If it returns nil, then a List control
  549. ** wasn't hit.  If it returns non-nil, then it was a List control that
  550. ** was hit, and specifically the one returned. */
  551.  
  552. ControlHandle    CLCtlHit(void)
  553. {
  554.     ControlHandle    ctl;
  555.  
  556.     ctl = gFoundViewCtl;
  557.     gFoundViewCtl = nil;
  558.     return(ctl);
  559. }
  560.  
  561.  
  562.  
  563. static ControlHandle    dummyCLCtlHit(void)
  564. {
  565.     return(nil);
  566. }
  567.  
  568.  
  569.  
  570. /*****************************************************************************/
  571.  
  572.  
  573.  
  574. /* Handle the event if it applies to the active List control.  If some
  575. ** action occured due to the event, return true. */
  576.  
  577. Boolean    CLEvent(WindowPtr window, EventRecord *event, short *action)
  578. {
  579.     WindowPtr    clickWindow;
  580.     short        actn;
  581.  
  582.     if (action)
  583.         *action = 0;
  584.  
  585.     switch(event->what) {
  586.  
  587.         case mouseDown:
  588.             if (FindWindow(event->where, &clickWindow) == inContent)
  589.                 if (window == clickWindow)
  590.                     if (((WindowPeek)window)->hilited) return(CLClick(window, event, action));
  591.             break;
  592.  
  593.         case autoKey:
  594.         case keyDown:
  595.             if (!(event->modifiers & cmdKey)) {
  596.                 actn = CLKey(window, event);
  597.                 if (action)
  598.                     *action = actn;
  599.                 if (actn) return(true);
  600.             }
  601.             break;
  602.     }
  603.  
  604.     return(false);
  605. }
  606.  
  607.  
  608.  
  609. /*****************************************************************************/
  610.  
  611.  
  612.  
  613. /* Returns the active List control, if any.  Unlike the TextEdit control, passing
  614. ** in nil doesn't return the currently active control independent of window.  The
  615. ** only reason that the TextEdit control returns the "globally active" control is
  616. ** so that the TEIdle procedure can do its thing.  The List control doesn't have
  617. ** such a thing, so there is no purpose for a "globally active" control.  If the
  618. ** window pointer passed in is nil (requesting the "globally active" List control),
  619. ** we just return nil, indicating that there isn't one. */
  620.  
  621. ListHandle    CLFindActive(WindowPtr window)
  622. {
  623.     ControlHandle    viewCtl;
  624.     ListHandle        list;
  625.     short            display;
  626.     CLDataHndl        listData;
  627.  
  628.     if (!window) return(nil);
  629.  
  630.     for (viewCtl = nil;;) {
  631.         viewCtl = CLNext(window, &list, viewCtl, 1, true);
  632.         if (!viewCtl) break;
  633.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  634.         display  = (*listData)->mode;
  635.         if (display & clActive) break;
  636.     }
  637.     return(list);
  638. }
  639.  
  640.  
  641.  
  642. static ListHandle    dummyCLFindActive(WindowPtr window)
  643. {
  644. #ifndef __MWERKS__
  645. #pragma unused (window)
  646. #endif
  647.     return(nil);
  648. }
  649.  
  650.  
  651.  
  652. /*****************************************************************************/
  653.  
  654.  
  655.  
  656. /* This determines if a List control was clicked on directly.  This does
  657. ** not determine if a related scrollbar was clicked on.  If a List
  658. ** control was clicked on, then true is returned, as well as the List
  659. ** handle and the handle to the view control. */
  660.  
  661. ControlHandle    CLFindCtl(WindowPtr window, EventRecord *event, ListHandle *listHndl, ControlHandle *ctlHit)
  662. {
  663.     WindowPtr        oldPort;
  664.     Point            mouseLoc;
  665.     ControlHandle    ctl, listctl;
  666.     ListHandle        list;
  667.  
  668.     if (window) {
  669.         GetPort(&oldPort);
  670.         SetPort(window);
  671.         mouseLoc = event->where;
  672.         GlobalToLocal(&mouseLoc);
  673.         SetPort(oldPort);
  674.  
  675.         gFoundLHndl = nil;
  676.  
  677.         if (!WhichControl(mouseLoc, 0, window, &ctl)) return(nil);
  678.             /* Didn't hit a thing, so forget it. */
  679.  
  680.         list = CLFromScroll(ctl, &listctl);
  681.         if (list) {
  682.             if (ctlHit)
  683.                 *ctlHit = ctl;
  684.             if (listHndl)
  685.                 *listHndl = list;
  686.             return(listctl);
  687.         }
  688.  
  689.         FindControl(mouseLoc, window, &ctl);
  690.         if (!ctl)                                                        return(nil);
  691.         if (StripAddress((*ctl)->contrlDefProc) != StripAddress(gCDEF)) return(nil);
  692.             /* Control hit was above List control, so we didn't hit a List control. */
  693.         if (ctlHit)
  694.             *ctlHit = ctl;
  695.         if (listHndl)
  696.             *listHndl = gFoundLHndl;
  697.         if (gFoundLHndl) return(ctl);
  698.     }
  699.  
  700.     if (listHndl)
  701.         *listHndl = nil;
  702.     if (ctlHit)
  703.         *ctlHit = nil;
  704.     return(nil);
  705. }
  706.  
  707.  
  708.  
  709. /*****************************************************************************/
  710.  
  711.  
  712.  
  713. /* Find the List record that is related to the indicated scrollbar. */
  714.  
  715. ListHandle    CLFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  716. {
  717.     WindowPtr        window;
  718.     ControlHandle    viewCtl;
  719.     ListHandle        list;
  720.  
  721.     *retCtl = nil;
  722.     if (!IsScrollBar(scrollCtl)) return(nil);
  723.  
  724.     window = (*scrollCtl)->contrlOwner;
  725.  
  726.     for (*retCtl = viewCtl = nil;;) {
  727.         viewCtl = CLNext(window, &list, viewCtl, 1, false);
  728.         if (!viewCtl) return(nil);
  729.         list = (ListHandle)GetControlReference(viewCtl);
  730.         if (
  731.             ((*list)->vScroll == scrollCtl) || 
  732.             ((*list)->hScroll == scrollCtl)
  733.         ) {
  734.             *retCtl = viewCtl;
  735.             return(list);
  736.         }
  737.     }
  738. }
  739.  
  740.  
  741.  
  742. /*****************************************************************************/
  743.  
  744.  
  745.  
  746. /* Get the Nth List control in the control list of a window. */
  747.  
  748. ListHandle    CLGetList(WindowPtr window, short lnum)
  749. {
  750.     ControlHandle    ctl;
  751.     ListHandle        list;
  752.  
  753.     for (ctl = nil; lnum--; ctl = CLNext(window, &list, ctl, 1, false)) {};
  754.     return(list);
  755. }
  756.  
  757.  
  758.  
  759. /*****************************************************************************/
  760.  
  761.  
  762.  
  763. /* Insert a cell alphabetically into the list.  Whichever parameter is passed in
  764. ** as -1, either row or column, that is the dimension that is determined. */
  765.  
  766. short    CLInsert(ListHandle listHndl, char *data, short dataLen, short row, short col)
  767. {
  768.     short            loc, len;
  769.     Point            cell;
  770.     char            cstr[256];
  771.     CLDataHndl        listData;
  772.     ControlHandle    viewCtl;
  773.  
  774.     if (!listHndl) return(-1);
  775.  
  776.     gGetCompareDataProc = nil;
  777.     viewCtl = CLViewFromList(listHndl);
  778.     if (viewCtl) {
  779.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  780.         if (listData)
  781.             gGetCompareDataProc = (*listData)->getCompareData;
  782.     }
  783.  
  784.     if (gGetCompareDataProc) {
  785.         (*gGetCompareDataProc)(data, dataLen, cstr, &len);
  786.         loc = CLRowOrColSearch(listHndl, cstr, len, row, col);
  787.     }
  788.     else loc = CLRowOrColSearch(listHndl, data, dataLen, row, col);
  789.  
  790.     if (row == -1) {
  791.         LAddRow(1, cell.v = loc, listHndl);
  792.         cell.h = col;
  793.     }
  794.     else {
  795.         LAddColumn(1, cell.h = loc, listHndl);
  796.         cell.v = row;
  797.     }
  798.  
  799.     LSetCell(data, dataLen, cell, listHndl);
  800.     return(loc);
  801. }
  802.  
  803.  
  804.  
  805. /*****************************************************************************/
  806.  
  807.  
  808.  
  809. /* See if the keypress event applies to the List control, and if it does,
  810. ** handle it and return true. */
  811.  
  812. Boolean    CLKey(WindowPtr window, EventRecord *event)
  813. {
  814.     ListHandle            list;
  815.     ControlHandle        listCtl;
  816.     short                key, mode, thresh;
  817.     long                ll;
  818.     Point                cell, dcell, mm, oldCell, lastCell;
  819.     Rect                bnds, visCells, rct;
  820.     CLDataHndl            listData;
  821.     CLKeyFilterProcPtr    kproc;
  822.  
  823.     list = CLFindActive(window);
  824.     if (list) {
  825.  
  826.         listCtl  = CLViewFromList(list);
  827.         listData = (CLDataHndl)(*listCtl)->contrlData;
  828.         mode     = (*listData)->mode;
  829.         if (!(mode & clKeyPos)) return(false);
  830.  
  831.         kproc = (*listData)->keyFilter;
  832.         if (kproc)
  833.             if ((*kproc)(list, event))
  834.                 return(true);
  835.  
  836.         bnds = (*list)->dataBounds;
  837.         if (bnds.top == bnds.bottom) return(true);
  838.         if (bnds.left == bnds.right) return(true);
  839.             /* The list is empty, so whatever was typed has been "handled". */
  840.  
  841.         if (mode & clVariable) {
  842.             ++bnds.top;
  843.             ++bnds.left;
  844.         }
  845.  
  846.         cell.h = bnds.left;
  847.         cell.v = bnds.top;
  848.         key = event->message & charCodeMask;
  849.  
  850.         if ((key >= chLeft) && (key <= chDown)) {
  851.             if (LGetSelect(true, &cell, list)) key -= kPrevSel;
  852.             else                               cell.h = cell.v = -1;
  853.             for (oldCell = lastCell = cell;; lastCell = cell) {
  854.                 switch (key) {
  855.                     case chLeft - kPrevSel:
  856.                         if (cell.h > bnds.left)
  857.                             --cell.h;
  858.                         break;
  859.                     case chRight - kPrevSel:
  860.                         if (cell.h < bnds.right - 1)
  861.                             ++cell.h;
  862.                         break;
  863.                     case chUp - kPrevSel:
  864.                         if (cell.v > bnds.top)
  865.                             --cell.v;
  866.                         break;
  867.                     case chDown - kPrevSel:
  868.                         if (cell.v < bnds.bottom - 1)
  869.                             ++cell.v;
  870.                         break;
  871.                     case chLeft:
  872.                         cell.v = (*list)->visible.top;
  873.                         cell.h = bnds.right - 1;
  874.                         break;
  875.                     case chRight:
  876.                         cell.v = (*list)->visible.top;
  877.                         cell.h = bnds.left;
  878.                         break;
  879.                     case chUp:
  880.                         cell.h = (*list)->visible.left;
  881.                         cell.v = bnds.bottom - 1;
  882.                         break;
  883.                     case chDown:
  884.                         cell.h = (*list)->visible.left;
  885.                         cell.v = bnds.top;
  886.                         break;
  887.                 }
  888.                 if (!(mode & clVariable)) break;
  889.                 if ((cell.h == lastCell.h) && (cell.v == lastCell.v)) break;
  890.                 (*gclvGetCellRect)(list, cell.h, cell.v, &rct);
  891.                 if (!EmptyRect(&rct)) break;
  892.                 if (key > (chDown - kPrevSel)) key -= kPrevSel;
  893.             }
  894.             if (mode & clVariable) {
  895.                 (*gclvGetCellRect)(list, cell.h, cell.v, &rct);
  896.                 if (EmptyRect(&rct)) cell = oldCell;
  897.                 if (cell.h == -1) return(true);
  898.             }
  899.         }
  900.         else {
  901.             thresh = LMGetKeyThresh();;
  902.             if (thresh > 60) thresh = 60;
  903.             thresh <<= 1;
  904.             if (gLastKeyTime + thresh < event->when)        /* Too long, reset char collection. */
  905.                 gListPosTextLen = 0;
  906.             gLastKeyTime = event->when;
  907.             if (gListPosTextLen < kListPosTextLen)
  908.                 gListPosText[gListPosTextLen++] = key;
  909.             gGetCompareDataProc = (*listData)->getCompareData;
  910.             gDoCompareDataProc  = (*listData)->doCompareData;
  911.             if (!LGetSelect(true, &cell, list)) cell.h = (*list)->visible.left;
  912.             cell.v = CLRowOrColSearch(list, gListPosText, gListPosTextLen, -1, cell.h);
  913.             if (cell.v >= bnds.bottom) cell.v = bnds.bottom - 1;
  914.         }
  915.  
  916.         UseControlStyle(listCtl);
  917.  
  918.         if (mode & clVariable)
  919.             (*gclvSetSelect)(true, cell, list);
  920.         else
  921.             LSetSelect(true, cell, list);        /* Select cell that is closest. */
  922.  
  923.         dcell.h = bnds.left;
  924.         dcell.v = bnds.top;
  925.         for (;;) {                /* Deselect old cells. */
  926.             if (!LGetSelect(true, &dcell, list)) break;
  927.             if ((dcell.h == cell.h) && (dcell.v == cell.v)) {
  928.                 if (++dcell.h >= bnds.right) {
  929.                     dcell.h = bnds.left;
  930.                     ++dcell.v;
  931.                 }
  932.             }
  933.             else {
  934.                 if (mode & clVariable)
  935.                     (*gclvSetSelect)(false, dcell, list);
  936.                 else
  937.                     LSetSelect(false, dcell, list);
  938.             }
  939.         }
  940.         UseControlStyle(nil);
  941.  
  942.         visCells = (*list)->visible;
  943.         if (PtInRect(cell, &visCells)) return(true);        /* Already in view. */
  944.  
  945.         UseControlStyle(listCtl);            /* Scroll into view the way we want it done. */
  946.         if (mode & clVariable) (*gclvAutoScroll)(list);
  947.         else {
  948.             ll = PinRect(&visCells, cell);
  949.             mm = *(Point *)≪
  950.             LScroll(cell.h - mm.h, cell.v - mm.v, list);
  951.         }
  952.         UseControlStyle(nil);
  953.         return(true);
  954.     }
  955.  
  956.     return(false);
  957. }
  958.  
  959.  
  960.  
  961. static Boolean    dummyCLKey(WindowPtr window, EventRecord *event)
  962. {
  963. #ifndef __MWERKS__
  964. #pragma unused (window, event)
  965. #endif
  966.     return(false);
  967. }
  968.  
  969.  
  970.  
  971. /*****************************************************************************/
  972.  
  973.  
  974.  
  975. /* Create a new List control.  See the comments at the beginning of this
  976. ** file for more information.  Note that this function doesn't get a dummy,
  977. ** as you really mean to have this code if you call it.  It calls CLInitialize(),
  978. ** just to make sure that the proc pointers are set to point to the real function,
  979. ** instead of the dummy functions.  By having CLNew() call CLInitialize(), the
  980. ** application won't have to worry about calling CLInitialize().  By the time that
  981. ** the application is using a List control, the proc pointers will be initialized. */
  982.  
  983. ListHandle    CLNew(short viewID, Boolean vis, Rect *vrct, short numRows, short numCols,
  984.                   short cellHeight, short cellWidth, short theLProc, WindowPtr window, short mode)
  985. {
  986.     WindowPtr        oldPort;
  987.     Rect            viewRct, dataBnds, rct;
  988.     Point            cellSize;
  989.     ListHandle        list;
  990.     Boolean            err;
  991.     ControlHandle    viewCtl;
  992.     Boolean            drawIt, hasGrow;
  993.     short            hScroll, vScroll;
  994.     CLDataHndl        listData;
  995.     ControlHandle    ctl;
  996.     RgnHandle        oldClip, newClip;
  997.  
  998.     static ControlDefUPP    cdefupp;
  999.  
  1000.     CLInitialize();            /* Make sure that this code gets linked in, in case the application
  1001.                             ** is using the proc pointers.  CtlHandler.c uses the proc pointers
  1002.                             ** so that if the application doesn't use List controls, this code
  1003.                             ** won't get linked in. */
  1004.     GetPort(&oldPort);
  1005.     SetPort(window);
  1006.  
  1007.     viewRct         = *vrct;
  1008.     dataBnds.top    = dataBnds.left = 0;
  1009.     dataBnds.right  = numCols;
  1010.     dataBnds.bottom = numRows;
  1011.  
  1012.     cellSize.h = cellWidth;
  1013.     cellSize.v = cellHeight;
  1014.  
  1015.     drawIt  = (mode & clDrawIt)  ? 1 : 0;
  1016.     hScroll = (mode & clHScroll) ? 1 : 0;
  1017.     vScroll = (mode & clVScroll) ? 1 : 0;
  1018.     hasGrow = (mode & clHasGrow) ? 1 : 0;
  1019.  
  1020.     viewCtl = nil;
  1021.  
  1022.     if (!vis) {
  1023.         GetClip(oldClip = NewRgn());
  1024.         SetClip(newClip = NewRgn());
  1025.     }
  1026.     list = LNew(&viewRct, &dataBnds, cellSize, theLProc, window, false, hasGrow, hScroll, vScroll);
  1027.     if (!vis) {
  1028.         if (list) {
  1029.             if ((*list)->hScroll) HideControl((*list)->hScroll);
  1030.             if ((*list)->vScroll) HideControl((*list)->vScroll);
  1031.         }
  1032.         SetClip(oldClip);
  1033.         DisposeRgn(oldClip);
  1034.         DisposeRgn(newClip);
  1035.     }
  1036.  
  1037.     err = false;
  1038.     if (list) {        /* If we were able to create the List record... */
  1039.  
  1040.         if ((hasGrow) && (hScroll + vScroll == 1)) {
  1041.             ctl = (*list)->hScroll;
  1042.             if (ctl) {
  1043.                 rct = (*ctl)->contrlRect;
  1044.                 if (rct.right >= viewRct.right)
  1045.                     SizeControl(ctl, viewRct.right - viewRct.left - 13, rct.bottom - rct.top);
  1046.             }
  1047.             ctl = (*list)->vScroll;
  1048.             if (ctl) {
  1049.                 rct = (*ctl)->contrlRect;
  1050.                 if (rct.bottom >= viewRct.bottom)
  1051.                     SizeControl(ctl, rct.right - rct.left, viewRct.bottom - viewRct.top - 13);
  1052.             }
  1053.         }
  1054.  
  1055.         if (!gCDEF) {
  1056.             gCDEF = (cdefRsrcJMPHndl)GetResource('CDEF', (viewID / 16));
  1057.             if (gCDEF) {
  1058.                 if (!cdefupp) {
  1059.                     cdefupp = NewControlDefProc(CLCtl);
  1060.                 }
  1061.                 (*gCDEF)->jmpAddress = (long)cdefupp;
  1062.                 if (TrapExists(_HWPriv))
  1063.                     FlushInstructionCache();
  1064.                         /* Make sure that instruction caches don't kill us. */
  1065.             }
  1066.             else err = true;
  1067.         }
  1068.  
  1069.         if (!err)
  1070.             viewCtl = NewControl(window, &viewRct, "\p", false, 0, 0, 0, viewID, (long)list);
  1071.                 /* Use our custom view cdef.  It's wierd, but it's small. */
  1072.                 /* We have to create the control initially invisible because we haven't */
  1073.                 /* initialized all of the data needed by the update procedure.  If it   */
  1074.                 /* is created visible, the update procedure will be immediately called, */
  1075.                 /* and this would be very bad to do until all of the data is there.     */
  1076.  
  1077.         mode &= (0xFFFF - clDrawIt);
  1078.  
  1079.         if (!viewCtl) err = true;
  1080.  
  1081.         if (!err) {
  1082.             (*viewCtl)->contrlData = nil;
  1083.             listData = (CLDataHndl)NewHandleClear(sizeof(CLDataRec));
  1084.             if (listData) {
  1085.                 (*listData)->mode      = mode & (0xFFFF - clVariable);
  1086.                 (*viewCtl)->contrlData = (Handle)listData;
  1087.                 if (mode & clVariable)
  1088.                     if (gclvVariableSizeCells)
  1089.                         (*gclvVariableSizeCells)(list);
  1090.                 if (vis) {
  1091.                     LSetDrawingMode(drawIt, list);
  1092.                     if (vis) ShowStyledControl(viewCtl);
  1093.                         /* Now that the data is initialized, we can show the control. */
  1094.                 }
  1095.                 else
  1096.                     if (drawIt)
  1097.                         (*list)->listFlags &= (0xFFFF - 0x08);
  1098.                             /* The silly ListMgr shows the scrollbars if we just call LDoDraw. */
  1099.                 if (mode & clActive)
  1100.                     CLActivate(true, list);
  1101.             }
  1102.             else err = true;
  1103.         }
  1104.     }
  1105.     else err = true;
  1106.  
  1107.     SetPort(oldPort);
  1108.  
  1109.     if (err) {        /* Oops.  Somebody wasn't happy. */
  1110.         if (viewCtl)
  1111.             DisposeControl(viewCtl);
  1112.                 /* This also disposes of the List handle! */
  1113.         else
  1114.             if (list)
  1115.                 LDispose(list);
  1116.                     /* We have to dispose of the List handle ourselves if
  1117.                     ** creating the view control failed. */
  1118.  
  1119.         list = nil;        /* Return that there is no List control. */
  1120.     }
  1121.  
  1122.     return(list);
  1123. }
  1124.  
  1125.  
  1126.  
  1127. /*****************************************************************************/
  1128.  
  1129.  
  1130.  
  1131. /* Get the next List control in the window.  You pass it a control handle
  1132. ** for the view control, or nil to start at the beginning of the window.
  1133. ** It returns both a List handle and the view control handle for that
  1134. ** List record.  If none is found, nil is returned.  This allows you to
  1135. ** repeatedly call this function and walk through all the List controls
  1136. ** in a window. */
  1137.  
  1138. ControlHandle    CLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive)
  1139. {
  1140.     ControlHandle    nextCtl, priorCtl;
  1141.  
  1142.     if (listHndl)
  1143.         *listHndl = nil;
  1144.  
  1145.     if (!window) return(nil);
  1146.     if (!gCDEF)  return(nil);
  1147.  
  1148.     if (dir > 0) {
  1149.         if (!ctl)
  1150.             ctl = ((WindowPeek)window)->controlList;
  1151.         else
  1152.             ctl = (*ctl)->nextControl;
  1153.         while (ctl) {
  1154.             if ((!justActive) || ((*ctl)->contrlVis)) {
  1155.                 if ((!justActive) || ((*ctl)->contrlHilite != 255)) {
  1156.                     if (StripAddress((*ctl)->contrlDefProc) == StripAddress(gCDEF)) {
  1157.                             /* The handle may be locked, which means that the hi-bit
  1158.                             ** may be on, thus invalidating the compare.  Dereference the
  1159.                             ** handles to get rid of this possibility. */
  1160.                         if (listHndl)
  1161.                             *listHndl = (ListHandle)GetControlReference(ctl);
  1162.                         return(ctl);
  1163.                     }
  1164.                 }
  1165.             }
  1166.             ctl = (*ctl)->nextControl;
  1167.         }
  1168.         return(ctl);
  1169.     }
  1170.  
  1171.     nextCtl = ((WindowPeek)window)->controlList;
  1172.     for (priorCtl = nil; ;nextCtl = (*nextCtl)->nextControl) {
  1173.         if ((!nextCtl) || (nextCtl == ctl)) {
  1174.             if (priorCtl)
  1175.                 if (listHndl)
  1176.                     *listHndl = (ListHandle)GetControlReference(priorCtl);
  1177.             return(priorCtl);
  1178.         }
  1179.         if ((!justActive) || ((*nextCtl)->contrlVis)) {
  1180.             if ((!justActive) || ((*nextCtl)->contrlHilite != 255)) {
  1181.                 if (StripAddress((*nextCtl)->contrlDefProc) == StripAddress(gCDEF))
  1182.                     priorCtl = nextCtl;
  1183.                         /* The handle may be locked, which means that the hi-bit
  1184.                         ** may be on, thus invalidating the compare.  Dereference the
  1185.                         ** handles to get rid of this possibility. */
  1186.             }
  1187.         }
  1188.     }
  1189. }
  1190.  
  1191.  
  1192.  
  1193. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl, short dir, Boolean justActive)
  1194. {
  1195. #ifndef __MWERKS__
  1196. #pragma unused (window, ctl, dir, justActive)
  1197. #endif
  1198.     *listHndl = nil;
  1199.     return(nil);
  1200. }
  1201.  
  1202.  
  1203.  
  1204. /*****************************************************************************/
  1205.  
  1206.  
  1207.  
  1208. /* From the starting for or column, print as many cells as will fit into the
  1209. ** designated rect.  Pass in a starting row and column, and they will be
  1210. ** adjusted to indicate the first cell that didn't fit into the rect.  If all
  1211. ** remaining cells were printed, the row is returned as -1.  The bottom of the
  1212. ** rect to print in is also adjusted to indicate where the actual cut-off
  1213. ** point was. */
  1214.  
  1215. void    CLPrint(RgnHandle clipRgn, ListHandle listHndl, short *row, short *col,
  1216.                 short leftEdge, Rect *drawRct)
  1217. {
  1218.     Rect        dataBnds, keepView, keepVis;
  1219.     Point        csize;
  1220.     short        h, v;
  1221.     RgnHandle    rgn;
  1222.  
  1223.     if (!listHndl) return;
  1224.  
  1225.     dataBnds = (*listHndl)->dataBounds;
  1226.     if ((*col < dataBnds.left) || (*col >= dataBnds.right))
  1227.         *col = leftEdge;
  1228.     if (*row < dataBnds.top)
  1229.         *row = dataBnds.top;
  1230.     if (*row >= dataBnds.bottom) {
  1231.         *row = -1;
  1232.         return;
  1233.     }
  1234.  
  1235.     keepView = (*listHndl)->rView;
  1236.     csize    = (*listHndl)->cellSize;
  1237.     keepVis  = (*listHndl)->visible;
  1238.  
  1239.     h = (drawRct->right - drawRct->left) / csize.h;
  1240.     if (!h)
  1241.         ++h;
  1242.     v = (drawRct->bottom - drawRct->top) / csize.v;
  1243.     if (!v)
  1244.         ++v;
  1245.  
  1246.     if (*col + h > dataBnds.right)
  1247.         h = dataBnds.right  - *col;
  1248.     if (*row + v > dataBnds.bottom)
  1249.         v = dataBnds.bottom - *row;
  1250.  
  1251.     drawRct->bottom = drawRct->top + v * csize.v;
  1252.  
  1253.     (*listHndl)->rView = *drawRct;
  1254.     (*listHndl)->visible.right  = ((*listHndl)->visible.left = *col) + h;
  1255.     (*listHndl)->visible.bottom = ((*listHndl)->visible.top  = *row) + v;
  1256.  
  1257.     if (!(rgn = clipRgn)) {
  1258.         rgn = NewRgn();
  1259.         RectRgn(rgn, drawRct);
  1260.     }
  1261.     CLUpdate(rgn, listHndl);
  1262.     if (!clipRgn)
  1263.         DisposeRgn(rgn);
  1264.  
  1265.     (*listHndl)->rView   = keepView;
  1266.     (*listHndl)->visible = keepVis;
  1267.  
  1268.     *col += h;
  1269.     if (*col >= dataBnds.right) {
  1270.         *col = leftEdge;
  1271.         *row += v;
  1272.         if (*row >= dataBnds.bottom)
  1273.             *row = -1;
  1274.     }
  1275. }
  1276.  
  1277.  
  1278.  
  1279. /*****************************************************************************/
  1280.  
  1281.  
  1282.  
  1283. /* Find the location in the list where the data would belong if inserted.  The row
  1284. ** and column are passed in.  If either is -1, that is the dimension that will be
  1285. ** determined and returned. */
  1286.  
  1287. short    CLRowOrColSearch(ListHandle listHndl, void *data, short dataLen, short row, short col)
  1288. {
  1289.     ControlHandle    viewCtl;
  1290.     Rect            dataBnds;
  1291.     short            numCells, baseCell, pow, loc, cdataLen, varSize;
  1292.     Point            cell;
  1293.     Str255            cdata, gcdata;
  1294.     CLDataHndl        listData;
  1295.  
  1296.     if (!listHndl) return(-1);
  1297.  
  1298.     gGetCompareDataProc = nil;
  1299.     gDoCompareDataProc  = nil;
  1300.  
  1301.     varSize = 0;
  1302.     viewCtl = CLViewFromList(listHndl);
  1303.     if (viewCtl) {
  1304.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  1305.         if (listData) {
  1306.             gGetCompareDataProc = (*listData)->getCompareData;
  1307.             gDoCompareDataProc  = (*listData)->doCompareData;
  1308.             if ((*listData)->mode & clVariable) ++varSize;
  1309.         }
  1310.     }
  1311.  
  1312.     dataBnds = (*listHndl)->dataBounds;
  1313.     if (row == -1)
  1314.         numCells = dataBnds.bottom - (baseCell = dataBnds.top);
  1315.     else
  1316.         numCells = dataBnds.right  - (baseCell = dataBnds.left);
  1317.             /* Get some reference info on the size/start of the list. */
  1318.  
  1319.     if (varSize) {
  1320.         ++baseCell;
  1321.         --numCells;
  1322.     }
  1323.  
  1324.     cell.v = cell.h = varSize;
  1325.  
  1326.     for (;numCells > baseCell; --numCells) {        /* Exclude empty end cells. */
  1327.         if (row == -1)
  1328.             cell.v = baseCell + numCells - 1;
  1329.         else
  1330.             cell.h = baseCell + numCells - 1;
  1331.         cdataLen = 1;
  1332.         LGetCell(cdata, &cdataLen, cell, listHndl);
  1333.         if (cdataLen) break;
  1334.     }
  1335.  
  1336.     if (numCells) {
  1337.         if (row != -1)
  1338.             cell.v = row;
  1339.         if (col != -1)
  1340.             cell.h = col;
  1341.         for (pow = 1; pow < numCells; pow <<= 1) {};
  1342.         pow >>= 1;        /* pow = 2^n such that pow < numCells. */
  1343.  
  1344.         for (loc = pow; pow;) {            /* Do binary search for where to insert. */
  1345.             if (loc >= numCells)
  1346.                 loc = numCells - 1;        /* Off the end is bad. */
  1347.             if (row == -1)
  1348.                 cell.v = baseCell + loc;
  1349.             else
  1350.                 cell.h = baseCell + loc;
  1351.             pow >>= 1;
  1352.  
  1353.             cdataLen = 255;
  1354.             LGetCell(cdata, &cdataLen, cell, listHndl);        /* Get cell data to compare against. */
  1355.             if (gGetCompareDataProc) {
  1356.                 (*gGetCompareDataProc)(cdata, cdataLen, gcdata, &cdataLen);
  1357.                 BlockMove(gcdata, cdata, cdataLen);
  1358.             }
  1359.  
  1360.             if (gDoCompareDataProc)        /* Adjust location based on compare result. */
  1361.                 loc += (pow * (*gDoCompareDataProc)(data, cdata, dataLen, cdataLen));
  1362.             else
  1363.                 loc += (pow * IUMagString(data, cdata, dataLen, cdataLen));
  1364.         }
  1365.  
  1366.         /* The binary search got us close, but not exact.  We may be off by one
  1367.         ** in either direction.  (The binary search can't position in front of
  1368.         ** the first cell in the list, for example.)  Do a linear compare from
  1369.         ** this point to find the correct cell we should insert in front of. */
  1370.  
  1371.  
  1372.         if (loc < numCells) {                    /* Move to the first duplicate. */
  1373.             for (; --loc >= 0;) {
  1374.                 if (row == -1) cell.v = baseCell + loc;
  1375.                 else           cell.h = baseCell + loc;
  1376.                 cdataLen = 255;
  1377.                 LGetCell(cdata, &cdataLen, cell, listHndl);
  1378.                 if (gDoCompareDataProc) {
  1379.                     if ((*gDoCompareDataProc)(data, cdata, dataLen, cdataLen)) break;
  1380.                 }
  1381.                 else {
  1382.                     if (IUMagString(data, cdata, dataLen, cdataLen)) break;
  1383.                 }
  1384.             }
  1385.             ++loc;
  1386.         }
  1387.  
  1388.         if (loc) --loc;                /* Start linear search one back. */
  1389.         for (;; ++loc) {
  1390.             if (row == -1)
  1391.                 cell.v = baseCell + loc;
  1392.             else
  1393.                 cell.h = baseCell + loc;
  1394.             if (loc >= numCells) break;
  1395.             cdataLen = 255;
  1396.             LGetCell(cdata, &cdataLen, cell, listHndl);
  1397.             if (gGetCompareDataProc) {
  1398.                 (*gGetCompareDataProc)(cdata, cdataLen, gcdata, &cdataLen);
  1399.                 BlockMove(gcdata, cdata, cdataLen);
  1400.             }
  1401.  
  1402.             if (gDoCompareDataProc) {
  1403.                 if ((*gDoCompareDataProc)(data, cdata, dataLen, cdataLen) < 1) break;
  1404.             }
  1405.             else {
  1406.                 if (IUMagString(data, cdata, dataLen, cdataLen) < 1) break;
  1407.             }        /* If we are less than or equal to this cell, we have
  1408.                     ** found our insertion point, so break. */
  1409.         }
  1410.     }
  1411.  
  1412.     if (row == -1) return(cell.v);
  1413.     else           return(cell.h);
  1414. }
  1415.  
  1416.  
  1417.  
  1418. /*****************************************************************************/
  1419.  
  1420.  
  1421.  
  1422. /* Draw the List control in the correct form. */
  1423.  
  1424. void    CLUpdate(RgnHandle clipRgn, ListHandle listHndl)
  1425. {
  1426.     WindowPtr        curPort, listPort;
  1427.     ControlHandle    ctl;
  1428.     Rect            view, vis, bnds, r;
  1429.     RgnHandle        rgn1, rgn2;
  1430.     CLDataHndl        listData;
  1431.     short            mode;
  1432.  
  1433.     if (listHndl) {
  1434.  
  1435.         ctl = CLViewFromList(listHndl);
  1436.         if (ctl) {
  1437.  
  1438.             GetPort(&curPort);
  1439.  
  1440.             listPort = (*listHndl)->port;
  1441.             (*listHndl)->port = curPort;
  1442.  
  1443.             listData = (CLDataHndl)(*ctl)->contrlData;
  1444.             mode     = (*listData)->mode;
  1445.             if (mode & clVariable) (*gclvUpdate)(clipRgn, listHndl);
  1446.             else                   LUpdate(clipRgn, listHndl);
  1447.  
  1448.             (*listHndl)->port = listPort;
  1449.  
  1450.             view = (*listHndl)->rView;
  1451.             vis  = (*listHndl)->visible;
  1452.             bnds = (*listHndl)->dataBounds;
  1453.             SectRect(&vis, &bnds, &vis);
  1454.  
  1455.             r = view;
  1456.             if (mode & clVariable) {
  1457.                 if ((--vis.right > 0) && (--vis.bottom > 0)) {
  1458.                     (*gclvGetCellRect)(listHndl, vis.right, vis.bottom, &r);
  1459.                     r.top  = view.top;
  1460.                     r.left = view.left;
  1461.                 }
  1462.                 else SetRect(&r, 0, 0, 0, 0);
  1463.             }
  1464.             else {
  1465.                 r.right  = r.left + (vis.right  - vis.left) * (*listHndl)->cellSize.h;
  1466.                 r.bottom = r.top  + (vis.bottom - vis.top ) * (*listHndl)->cellSize.v;
  1467.             }
  1468.             SectRect(&r, &view, &r);
  1469.  
  1470.             RectRgn(rgn1 = NewRgn(), &view);
  1471.             RectRgn(rgn2 = NewRgn(), &r);
  1472.             DiffRgn(rgn1, rgn2, rgn1);
  1473.             EraseRgn(rgn1);
  1474.             DisposeRgn(rgn1);
  1475.             DisposeRgn(rgn2);
  1476.         }
  1477.     }
  1478. }
  1479.  
  1480.  
  1481.  
  1482. /*****************************************************************************/
  1483.  
  1484.  
  1485.  
  1486. /* Return the control handle for the view control that owns the List
  1487. ** record.  Use this to find the view to do customizations such as changing
  1488. ** the update procedure for this List control. */
  1489.  
  1490. ControlHandle    CLViewFromList(ListHandle listHndl)
  1491. {
  1492.     WindowPtr        window;
  1493.     ControlHandle    viewCtl;
  1494.     ListHandle        list;
  1495.  
  1496.     if (!listHndl) return(nil);
  1497.  
  1498.     if (gVFLView)
  1499.         if (gVFLList == listHndl)
  1500.             return(gVFLView);
  1501.  
  1502.     window = (WindowPtr)(*listHndl)->port;
  1503.     for (viewCtl = nil;;) {
  1504.         viewCtl = CLNext(window, &list, viewCtl, 1, false);
  1505.         if ((!viewCtl) || (list == listHndl)) return(viewCtl);
  1506.     }
  1507. }
  1508.  
  1509.  
  1510.  
  1511. static ControlHandle    dummyCLViewFromList(ListHandle listHndl)
  1512. {
  1513. #ifndef __MWERKS__
  1514. #pragma unused (listHndl)
  1515. #endif
  1516.     return(nil);
  1517. }
  1518.  
  1519.  
  1520.  
  1521. /*****************************************************************************/
  1522.  
  1523.  
  1524.  
  1525. /* This window is becoming active or inactive.  The borders of the List
  1526. ** controls need to be redrawn due to this.  For each List control in the
  1527. ** window, redraw the active border. */
  1528.  
  1529. ListHandle    CLWindActivate(WindowPtr window, Boolean displayIt)
  1530. {
  1531.     ListHandle    list;
  1532.  
  1533.     gLastKeyTime = 0;        /* Restart the entry collection for Lists. */
  1534.  
  1535.     if (!window) return(nil);
  1536.  
  1537.     list = CLFindActive(window);
  1538.     if (list) {
  1539.         if (displayIt)
  1540.             CLBorderDraw(list);
  1541.         return(list);
  1542.     }
  1543.  
  1544.     return(nil);
  1545. }
  1546.  
  1547.  
  1548.  
  1549. static ListHandle    dummyCLWindActivate(WindowPtr window, Boolean displayIt)
  1550. {
  1551. #ifndef __MWERKS__
  1552. #pragma unused (window, displayIt)
  1553. #endif
  1554.  
  1555.     return(nil);
  1556. }
  1557.  
  1558.  
  1559.  
  1560. /*****************************************************************************/
  1561.  
  1562.  
  1563.  
  1564. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
  1565. {
  1566.     short    cmp;
  1567.     char    cstr[256];
  1568.  
  1569.     if (gGetCompareDataProc) {
  1570.         (*gGetCompareDataProc)(aPtr, aLen, cstr, &aLen);
  1571.         aPtr = cstr;
  1572.     }
  1573.  
  1574.     if (gDoCompareDataProc)
  1575.         cmp = (*gDoCompareDataProc)(aPtr, bPtr, aLen, bLen);
  1576.     else
  1577.         cmp = IUMagString(aPtr, bPtr, aLen, bLen);
  1578.  
  1579.     if (cmp == -1) return(1);
  1580.     else           return(0);
  1581. }
  1582.  
  1583.  
  1584.  
  1585. /*****************************************************************************/
  1586.  
  1587.  
  1588.  
  1589. void    CLSize(ListHandle list, short newH, short newV)
  1590. {
  1591. #ifndef __MWERKS__
  1592. #pragma unused (oldh, oldv)
  1593. #endif
  1594.  
  1595.     WindowPtr        oldPort;
  1596.     ControlHandle    viewCtl, ctl;
  1597.     Rect            viewRct, rct;
  1598.     RgnHandle        oldClip, newClip;
  1599.     CLDataHndl        listData;
  1600.     short            mode, hScroll, vScroll;
  1601.  
  1602.     if (!(viewCtl = CLViewFromList(list))) return;
  1603.  
  1604.     GetPort(&oldPort);
  1605.     SetPort((*list)->port);
  1606.  
  1607.     viewRct  = (*viewCtl)->contrlRect;
  1608.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  1609.     mode     = (*listData)->mode;
  1610.  
  1611.     GetClip(oldClip = NewRgn());
  1612.     SetClip(newClip = NewRgn());
  1613.         /* Prevent any drawing, since the newly resized scrollbars may not account for
  1614.         ** the growIcon area.  We don't want them temporarily redrawing over the
  1615.         ** growIcon. */
  1616.  
  1617.     LSize(newH, newV, list);
  1618.  
  1619.     hScroll = (mode & clHScroll) ? 1 : 0;
  1620.     vScroll = (mode & clVScroll) ? 1 : 0;
  1621.         /* Get enough info to determine if we need to fix the scrollbar sizes. */
  1622.  
  1623.     viewRct.right  = viewRct.left + newH;
  1624.     viewRct.bottom = viewRct.top  + newV;
  1625.     (*viewCtl)->contrlRect = viewRct;
  1626.  
  1627.     if ((mode & clHasGrow) && (hScroll + vScroll == 1)) {
  1628.         ctl = (*list)->hScroll;
  1629.         if (ctl) {
  1630.             rct = (*ctl)->contrlRect;
  1631.             if (rct.right >= viewRct.right)
  1632.                 SizeControl(ctl, viewRct.right - viewRct.left - 13, rct.bottom - rct.top);
  1633.         }
  1634.         ctl = (*list)->vScroll;
  1635.         if (ctl) {
  1636.             rct = (*ctl)->contrlRect;
  1637.             if (rct.bottom >= viewRct.bottom)
  1638.                 SizeControl(ctl, rct.right - rct.left, viewRct.bottom - viewRct.top - 13);
  1639.         }
  1640.     }
  1641.  
  1642.     SetClip(oldClip);
  1643.     DisposeRgn(oldClip);
  1644.     DisposeRgn(newClip);
  1645.         /* Allow drawing again so we can redisplay the newly resized list control and scrollbars. */
  1646.  
  1647.     InsetRect(&viewRct, -1, -1);        /* Erase all of the old list control and scrollbars. */
  1648.     if (vScroll)
  1649.         viewRct.right += 15;
  1650.     if (hScroll)
  1651.         viewRct.bottom += 15;
  1652.     if (mode & clShowActive)
  1653.         InsetRect(&viewRct, -4, -4);
  1654.     if ((*viewCtl)->contrlVis)
  1655.         EraseRect(&viewRct);
  1656.  
  1657.     if (mode & clVariable) (*gclvAdjustScrollBars)(list);
  1658.  
  1659.     DoDraw1Control(viewCtl, false);
  1660.     SetPort(oldPort);
  1661. }
  1662.  
  1663.  
  1664.  
  1665. /*****************************************************************************/
  1666.  
  1667.  
  1668.  
  1669. void    CLMove(ListHandle list, short newH, short newV)
  1670. {
  1671. #ifndef __MWERKS__
  1672. #pragma unused (oldh, oldv)
  1673. #endif
  1674.  
  1675.     WindowPtr        oldPort;
  1676.     ControlHandle    viewCtl, ctl;
  1677.     Rect            viewRct, rct;
  1678.     RgnHandle        oldClip, newClip;
  1679.     CLDataHndl        listData;
  1680.     short            mode, hScroll, vScroll, dx, dy;
  1681.  
  1682.     if (!(viewCtl = CLViewFromList(list))) return;
  1683.  
  1684.     GetPort(&oldPort);
  1685.     SetPort((*list)->port);
  1686.  
  1687.     viewRct  = (*viewCtl)->contrlRect;
  1688.     listData = (CLDataHndl)(*viewCtl)->contrlData;
  1689.     mode     = (*listData)->mode;
  1690.  
  1691.     dx = newH - viewRct.left;
  1692.     dy = newV - viewRct.top;
  1693.     if ((!dx) && (!dy)) return;
  1694.  
  1695.     hScroll = (mode & clHScroll) ? 1 : 0;
  1696.     vScroll = (mode & clVScroll) ? 1 : 0;
  1697.         /* Get enough info to determine if we need to fix the scrollbar sizes. */
  1698.  
  1699.     rct = viewRct;                /* Erase all of the old list control and scrollbars. */
  1700.     InsetRect(&rct, -1, -1);
  1701.     if (vScroll)
  1702.         rct.right += 15;
  1703.     if (hScroll)
  1704.         rct.bottom += 15;
  1705.     if (mode & clShowActive)
  1706.         InsetRect(&rct, -4, -4);
  1707.     if ((*viewCtl)->contrlVis)
  1708.         EraseRect(&rct);
  1709.  
  1710.     OffsetRect(&viewRct, dx, dy);
  1711.     (*viewCtl)->contrlRect = viewRct;
  1712.     (*list)->rView = viewRct;
  1713.  
  1714.     GetClip(oldClip = NewRgn());
  1715.     SetClip(newClip = NewRgn());
  1716.  
  1717.     ctl = (*list)->hScroll;
  1718.     if (ctl) {
  1719.         rct = (*ctl)->contrlRect;
  1720.         MoveControl(ctl, rct.left + dx, rct.top + dy);
  1721.     }
  1722.  
  1723.     ctl = (*list)->vScroll;
  1724.     if (ctl) {
  1725.         rct = (*ctl)->contrlRect;
  1726.         MoveControl(ctl, rct.left + dx, rct.top + dy);
  1727.     }
  1728.  
  1729.     SetClip(oldClip);
  1730.     DisposeRgn(oldClip);
  1731.     DisposeRgn(newClip);
  1732.  
  1733.     DoDraw1Control(viewCtl, false);
  1734.     SetPort(oldPort);
  1735. }
  1736.  
  1737.  
  1738.  
  1739. /*****************************************************************************/
  1740.  
  1741.  
  1742.  
  1743. /* Show the designated List control and related scrollbars. */
  1744.  
  1745. void    CLShow(ListHandle list)
  1746. {
  1747.     ControlHandle    viewCtl, scrollCtl;
  1748.     short            i;
  1749.  
  1750.     viewCtl = CLViewFromList(list);
  1751.     if (viewCtl) {
  1752.         ShowStyledControl(viewCtl);
  1753.         for (i = 0; i < 2; i++) {
  1754.             scrollCtl = (i) ? (*list)->hScroll : (*list)->vScroll;
  1755.             if (scrollCtl)
  1756.                 ShowStyledControl(scrollCtl);
  1757.         }
  1758.     }
  1759. }
  1760.  
  1761.  
  1762.  
  1763. /*****************************************************************************/
  1764.  
  1765.  
  1766.  
  1767. /* Hide the designated List control and related scrollbars. */
  1768.  
  1769. Rect    CLHide(ListHandle list)
  1770. {
  1771.     ControlHandle    viewCtl, scrollCtl;
  1772.     short            i;
  1773.     WindowPtr        oldPort;
  1774.     CLDataHndl        listData;
  1775.     short            displayInfo;
  1776.     Rect            rct;
  1777.  
  1778.     viewCtl = CLViewFromList(list);
  1779.     if (viewCtl) {
  1780.         HideControl(viewCtl);
  1781.         for (i = 0; i < 2; i++) {
  1782.             scrollCtl = (i) ? (*list)->hScroll : (*list)->vScroll;
  1783.             if (scrollCtl)
  1784.                 HideControl(scrollCtl);
  1785.         }
  1786.     }
  1787.  
  1788.     GetPort(&oldPort);
  1789.     SetPort((*list)->port);
  1790.     listData    = (CLDataHndl)(*viewCtl)->contrlData;
  1791.     displayInfo = (*listData)->mode;
  1792.     rct         = (*list)->rView;
  1793.     InsetRect(&rct, -1, -1);
  1794.     if ((*list)->vScroll)
  1795.         rct.right  += 15;
  1796.     if ((*list)->hScroll)
  1797.         rct.bottom += 15;
  1798.     if (displayInfo & clActive)
  1799.         if (displayInfo & clShowActive)
  1800.             InsetRect(&rct, -3, -3);
  1801.  
  1802.     EraseRect(&rct);
  1803.     SetPort(oldPort);
  1804.  
  1805.     return(rct);
  1806. }
  1807.  
  1808.  
  1809.  
  1810.